Es existieren unterschiedliche Werkzeuge, um Architekturen und Softwareprojekt-Strukturen zu analysieren und zu validieren. Bei jqAssistant handelt es sich um ein neues interessantes Tool für diese Anwendungsfälle, das im Hintergrund mit der bekannten Graphendatenbank Neo4j und ihrer Abfragesprache Cypher arbeitet.
Es analysiert gegebene Projekte und Artefakte, speichert deren Metadaten und Relationen in der Graphendatenbank und eine Vielzahl unterschiedlicher Plugins reichern diese Datenbank dann um weitere Informationen an, die als Grundlage späterer Analysen und Validierungen dienen können.
Download und Installation
jqAssistant lässt sich entweder über die Kommandozeile bedienen oder über das Build-Tool Maven als Plugin in ein Projekt integrieren. Wir verwenden für die folgenden Beispiele das Kommandozeilen-Tool, das auf der entsprechenden Website als Jar-Datei zum Herunterladen verfügbar ist.
Nun steht uns für Linux-basierte Systeme mit bin/jqassistant.sh
bzw. für Windows-Systeme mit bin/jqassistant.cmd
alles zur Verfügung, was wir benötigen, um bestehende Strukturen analysieren und validieren zu können.
Graphendatenbank erzeugen
Mittels jqassistant.sh scan -f Verzeichnis
können wir nun alle Jar/Class-Files in einem Verzeichnis analysieren lassen und die Ergebnisse in eine Graphendatenbank schreiben, die uns dann für die nächsten Schritte als Ausgangspunkt dient.
Das Ergebnis kann so aussehen:
% bin/jqassistant.sh scan -f some-app.jar 2018-01-03 08:28:45.722 [main] INFO PluginConfigurationReaderImpl - Loaded jQAssistant plugins [CDI, Common, Core Analysis, EJB3, GraphML, JAX-RS, JPA 2, JSON, JUnit, Java, Java 8, Java EE 6, Maven 2 Repository, Maven 3, OSGi, RDBMS, TestNG, Tycho, XML, YAML]. 2018-01-03 08:28:46.073 [main] INFO StoreFactory - Connecting to store at 'file:/data/project/some-app/jqassistant/store' 2018-01-03 08:28:49.268 [main] INFO AbstractContainerScannerPlugin - Entering /data/project/some-app/target/some-app-2.7.0-SNAPSHOT.jar 2018-01-03 08:28:56.848 [main] INFO AbstractContainerScannerPlugin - Leaving /data/project/some-app/target/some-app-2.7.0-SNAPSHOT.jar (1791 entries, 7578 ms)
System im Browser erforschen
Um uns einen Eindruck vom zu analysierenden bzw. zu validierenden System zu verschaffen, können wir den Neo4j-Server samt GUI mit dem folgenden Befehl starten, sodass wir anschließend im Browser Abfragen auf die Datenbank durchführen können:
% bin/jqassistant.sh server 2018-01-03 08:35:14.973 [main] INFO PluginConfigurationReaderImpl - Loaded jQAssistant plugins [CDI, Common, Core Analysis, EJB3, GraphML, JAX-RS, JPA 2, JSON, JUnit, Java, Java 8, Java EE 6, Maven 2 Repository, Maven 3, OSGi, RDBMS, TestNG, Tycho, XML, YAML]. 2018-01-03 08:35:15.338 [main] INFO StoreFactory - Connecting to store at 'file:/data/project/some-app/jqassistant/store/' [..] 2018-01-03 08:35:19.232+0100 INFO [o.n.s.CommunityNeoServer] Remote interface ready and available at http://localhost:7474/ 2018-01-03 08:35:19.232 [main] INFO ServerTask - Running server 2018-01-03 08:35:19.232 [main] INFO ServerTask - Press <Enter> to finish.
Daraufhin können wir unter der Adresse http://localhost:7474/ unsere Datenbank im Browser durchsuchen. Wollen wir beispielsweise alle Klassen auffinden, die sich im Package net.seibertmedia
befinden, bewerkstelligen wir das mit der folgenden Abfrage:
MATCH (p:Package)-[:CONTAINS*]->(class:Class) WHERE p.fqn = "net.seibertmedia" RETURN p, class
Das Ganze stellt sich im Neo4j-Browser dann wie folgt dar:
Systeme validieren
Im letzten Abschnitt wollen wir näher auf die Möglichkeiten, mit jqAssistant Systeme zu validieren, eingehen. Nehmen wir für dieses beispielhafte Szenario einmal an, wir möchten verhindern, dass Klassen, die sich in einem vorgegebenen Package befinden, Methoden mit mehr als fünf Parametern definieren dürfen.
Zunächst erstellen wir eine Abfrage mittels Cypher, die solche Methoden auffindet:
MATCH (c:Class)-[:DECLARES]->(m:Method)-[p:HAS]->(:Parameter) WHERE c.fqn STARTS WITH "net.seibertmedia" WITH c.fqn AS class, m.name AS methodName, COUNT(p) AS params WHERE params > 5 RETURN class+"#"+methodName
Wir erstellen nun im Projektverzeichnis ein Unterverzeichnis jqassistant/rules
und speichern dort die folgende XML-Datei unter dem Namen custom-rule.xml
ab:
<jqa:jqassistant-rules xmlns:jqa="http://www.buschmais.com/jqassistant/core/analysis/rules/schema/v1.0"> <constraint id="seibert:DemoConstraint"> <description>Classes in the package 'net.seibertmedia' must not declare methods with more than 5 parameters.</description> <cypher><![CDATA[ MATCH (c:Class)-[:DECLARES]->(m:Method)-[p:HAS]->(:Parameter) WHERE c.fqn STARTS WITH "net.seibertmedia" WITH c.fqn AS class, m.name AS methodName, COUNT(p) AS params WHERE params > 5 RETURN class+"#"+methodName, params ]]></cypher> </constraint> <group id="default"> <includeConstraint refId="seibert:DemoConstraint" /> </group> </jqa:jqassistant-rules>
Jetzt können wir die Validierung mittels bin/jqassistant.sh analyze
starten - und wie zu erwarten war, schlägt die Validierung fehl und meldet uns drei Methoden, die mehr als fünf Parameter deklarieren:
% bin/jqassistant.sh analyze 2018-01-03 09:23:25.774 [main] INFO PluginConfigurationReaderImpl - Loaded jQAssistant plugins [CDI, Common, Core Analysis, EJB3, GraphML, JAX-RS, JPA 2, JSON, JUnit, Java, Java 8, Java EE 6, Maven 2 Repository, Maven 3, OSGi, RDBMS, TestNG, Tycho, XML, YAML]. 2018-01-03 09:23:26.128 [main] INFO StoreFactory - Connecting to store at 'file:/data/project/some-app/jqassistant/store/' 2018-01-03 09:23:28.998 [main] INFO AbstractAnalyzeTask - Executing analysis. 2018-01-03 09:23:29.010 [main] INFO AbstractAnalyzeTask - Reading rules from directory /data/project/some-app/jqassistant/rules 2018-01-03 09:23:29.107 [main] INFO AbstractAnalyzeTask - Executing group 'default' 2018-01-03 09:23:29.110 [main] INFO AbstractAnalyzeTask - Validating constraint 'seibert:DemoConstraint' with severity: 'MAJOR'. 2018-01-03 09:23:29.888 [main] INFO AbstractAnalyzeTask - Verifying results: failOnSeverity=MAJOR, warnOnSeverity=MINOR 2018-01-03 09:23:29.888 [main] ERROR AbstractAnalyzeTask - --[ Constraint Violation ]----------------------------------------- 2018-01-03 09:23:29.888 [main] ERROR AbstractAnalyzeTask - Constraint: seibert:DemoConstraint 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - Severity: MAJOR 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - Classes in the package 'net.seibertmedia' must not declare methods with more than 5 parameters. 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - class+"#"+methodName=net.seibertmedia.confluence.plugins.util.VelocityMacroHelper#renderConfluenceMacro, params=8 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - class+"#"+methodName=net.seibertmedia.confluence.plugins.theme.configuration.boundary.ThemeConfigurationWebservice#<init>, params=6 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - class+"#"+methodName=net.seibertmedia.confluence.aop.advice.LoggingAspect#logAdviceWithUser, params=6 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - ------------------------------------------------------------------- 2018-01-03 09:23:29.889 [main] ERROR AbstractAnalyzeTask - 2018-01-03 09:23:29.954 [main] ERROR Main - -> Failed rules detected: 0 concepts, 1 constraints
Für weitere Informationen und Metriken empfiehlt sich die Lektüre des folgenden Blog-Artikels und der ausführlichen technischen Dokumentation des Projekts.
Weiterführende Infos
Architekturregeln mit Java und ArchUnit sicherstellen
Analyse einer Java-Anwendung mit Java Mission Control und Flight Recorder
Kurzeinführung in das Testen von Web-Services mit Karate und JUnit
Was agile Software-Projekte dem Kunden bringen
Mehr über die Creative-Commons-Lizenz erfahren